/************************************************************************/
/*
you can get more refer from https://gitee.com/yiliangwu880/CppUtility.git
author:YiliangWu

任务功能：
	通用地实现了往玩家身上加入Task，然后侦听事件导致Task进度变化，Task完成。 
	不能直接复用，需要修改配置字段。

术语：
	{
		BaseTask：	   作业， 有类型，进度。     所有游戏任务，成就都是基于这个来实现
		Quest:	   游戏任务，可以关联一个Task实现，或者一个quest关联多个BaseTask， 全部BaseTask完成来计算完成。
		Achieve:   成就，类似Quest。
		TaskMgr：  管理全部BaseTask，控制BaseTask状态变化。
	}

注意： 
	用户需要确保 cfg, 对象生存期比 BaseTask 对象长。 一般cfg一直到进程结束才释放

其他：
	设计思想，本来配置信息可以做成模板，但代码会复杂。这里选修改代码的方式使用，一个项目通常就一次修改，就能把模板问题简化掉。

使用说明：
	1) 根据项目修改下配置TaskCfg的字段名。
	2) 给所有TaskType值加逻辑配置。 表达 task进度更新事件参数和TaskCfg的逻辑关系。 TaskTypeInfo 模板特例里面加。

用户代码参考：
	class MyTask: public BaseTask
	{
		Player &player;
	public:
		MyTask(const TaskCfg &cfg , Player &player, int64 num = 0);\
		:BaseTask(player.m_task_mgr, cfg, num)
		{
		}
		virtual void OnFinish() override
		{
			完成任务逻辑
		};

		virtual void OnUpdate()override
		{
			进度变化逻辑
		};
	};
	class Player
	{
		public:
			TaskMgr m_task_mgr; 
			map<uint64, unique_ptr<MyTask>> m_Question;


	};

	--------------------------
	//创建任务
	Player player;
	TaskCfg cfg; 
	player.m_Question.emplace({id, make_unique<MyTask>(cfg, player, 1)});//创建对象，自动注册到TaskMgr

	--------------------------
	//当触发任务改变事件发生时调用
	mgr:Update(TaskType::GET_ITEM, 1002, 1)；

	--------------------------
	//提前注销任务
	player.m_Question.erase(id); //TaskMgr关联的对象会自动注销


*/
/************************************************************************/

#pragma once

#include "../../svr_util/include/typedef.h"
#include "../../svr_util/include/misc.h"
#include <map>
#include <algorithm>
#include <array>
#include <vector>
#include <memory>
#include <stdarg.h>
#include "game_task_def.h"

//策划配置模板，根据不同项目改命名,字段
struct TaskCfg
{
	uint32 id;	                              //任务id,唯一就可以了
	uint32 task_type;		                  //TaskType
	uint32 num;								  //任务进度最大值
	uint32 para0, para1, para2, para3, para4; //选用参数
};


class TaskMgr;
//任务基类
//存库只需 m_num和 配置ID就完整了。
class BaseTask
{
	friend class TaskMgr;
private:
	su::WeakPtr<TaskMgr> m_mgr;
	int64 m_num;		  //表示进度
	const TaskCfg &m_cfg; 

public:
	//@num 任务初始化进度
	BaseTask(TaskMgr &mgr, const TaskCfg &cfg,  int64 num=0);
	virtual ~BaseTask();
	const TaskCfg &GetCfg() const { return m_cfg; }
	int64 GetNum() const { return m_num; }

	virtual void OnFinish()=0;
	virtual void OnUpdate()=0;
	
private:
	//noncopyable
	//  不能复制，因为构造对象就注册到TaskMgr了，避免临时对象注册到TaskMgr。
	BaseTask(const BaseTask&) = delete;
	BaseTask& operator= (const BaseTask&) = delete;
	BaseTask(BaseTask&&) = delete; //移动构造函数
	BaseTask& operator=(BaseTask&&) = delete;//移动赋值函数

};



//任务类型配置
//表达 task进度更新事件参数和TaskCfg参数的逻辑关系。 
struct TaskTypeCfg
{
	//vec_para_logic[0] 表示TaskMgr::Update para0的关系，  vec_para_logic[1]表示TaskMgr::Update para1的关系， 。。。
	//不需要paraX等选用参数时，这里就为empty
	std::vector<TaskParaOpt> vec_para_opt;

	//表示从Update传入的最后一个参数使用方式。 false表示累加值. true表示绝对值，
	bool is_last_para_absolute = false;


	TaskTypeCfg(const std::vector<std::string>& vecOpt = {}, bool bAbsolute = false)
	{
		for (auto& v : vecOpt)
		{
			vec_para_opt.push_back(StrToTaskParaOpt(v));
		}
		is_last_para_absolute = bAbsolute;
	}

	static TaskParaOpt StrToTaskParaOpt(const std::string& s)
	{
		if (s == "==")
		{
			return TaskParaOpt::EQUAL;
		}
		else if (s == ">=")
		{
			return TaskParaOpt::GREATER;
		}
		else if (s == "<=")
		{
			return TaskParaOpt::LESS;
		}
		else
		{
			L_ERROR("unknow logic type %s", s.c_str());
			return TaskParaOpt::NONE;
		}
	}
};

class TaskMgr : public su::EnableWeakPtr<TaskMgr>
{
	friend class BaseTask;
private:
	using VecTask = std::vector<BaseTask *>;
	using ArrayTaskS = std::array<VecTask, (uint32)TaskType::MAX_LEN>; 

	ArrayTaskS m_all_task;	 //m_all_task[type][idx]= BaseTask *.  指针加入删除由 BaseTask 构造析构控制，不会野。
	bool m_is_updateing = false; //防错误调用

public:
	uint32 GetTaskNum() const;
	//@brief task进度更新事件。如果 Task 完成，会被注销
	//@para ..., 每个参数的意义根据 TaskTypeStrInfo 意义来决定
	//注意：每个参数必需为类型uint32
	template<TaskType task_type, class ... Args>
	void Update(Args&& ... args);
		
private:
	void RegTask(BaseTask& task);
	void UnregTask(BaseTask& task);
	uint32 GetCfgPara(const TaskCfg &cfg, uint32 idx) const;
	bool IsLogicOk(TaskParaOpt logic, int64 cur_num, int64 cfg_para) const;
	//判断任务条件是否符合, 根据 TaskTypeCfg， TaskCfg 和 参数列表比对，排除最后一个参数。
	//@target_type_info	为 TaskTypeCfg的一个元素
	//@args		Update传进来的可变参数。 格式为 para1, para2, para3 
	//return true表示符合条件
	template<TaskType task_type, class ... Args>
	bool IsMatchCondition(const TaskCfg &task_cfg, Args&& ... args) const;
};

template<TaskType task_type, class ... Args>
void TaskMgr::Update(Args&& ... args)
{
	//m_is_updateing 控制，保证了m_all_task 在Update中不会被别的地方 删除，增加元素
	if (m_is_updateing)
	{
		L_ERROR("recursive call");
		return;
	}
	if ((uint32_t)task_type >= (uint32_t)TaskType::MAX_LEN)
	{
		L_ERROR("error task_type %d", (uint32_t)task_type);
		return;
	}

	size_t needParaNum = 1;
 	// 判断 PARA_OPT_LIST 是否为 nullptr 类型或者不是数组类型
	if constexpr (std::is_array_v<decltype(TaskTypeInfo<task_type>::PARA_OPT_LIST)>)
	{
		#define MyArrayLen(array) (sizeof(array)/sizeof((array)[0]))
		needParaNum = MyArrayLen(TaskTypeInfo<task_type>::PARA_OPT_LIST)+1;
		#undef MyArrayLen
	}

	if (sizeof...(Args) != needParaNum)
	{
		L_ERROR("error para num, need %d, but %d", needParaNum, sizeof...(Args));
		return;
	}
	m_is_updateing = true;


	VecTask &vec_task = m_all_task[(uint32_t)task_type];
	// 使用折叠表达式获取最后一个参数
	std::array<uint32, sizeof...(Args)> params{static_cast<uint32>(std::forward<Args>(args))...};
	uint32 last_para = params.back();
	VecTask remove_task; //完成等删除的task
	//不符合条件退出, 符合改变进度
	for (BaseTask *base_task : vec_task)
	{
		const TaskCfg &task_cfg = base_task->GetCfg();
		//判断是否符合条件
		if(needParaNum>1)
		{
			if (!IsMatchCondition<task_type>(task_cfg, std::forward<Args>(args)...))
			{
				continue;
			}
		}

		//符合条件，判断最后一个参数
		if (TaskTypeInfo<task_type>::IS_ABSOLUTE)//is_last_para_absolute true表示最后一个参数表示绝对值，false 表示累加值. default 表示累加
		{
			base_task->m_num = last_para;
		}
		else
		{
			base_task->m_num += last_para;
		}
		if(base_task->m_num >= task_cfg.num)
		{
			base_task->m_num = task_cfg.num;
			remove_task.push_back(base_task);//finish
		}
		base_task->OnUpdate();
	}

	m_is_updateing = false;//解锁了，可以让用户注册新BaseTask了，修改m_all_task
	for (BaseTask * v : remove_task)
	{
		v->OnFinish();
		for (auto iter = vec_task.begin(); iter != vec_task.end(); ++iter)
		{
			if (*iter == v)
			{
				*iter = vec_task.back();
				vec_task.erase(vec_task.end() - 1);
				break;
			}
		}
	}
}

template<TaskType task_type, class ... Args>
bool TaskMgr::IsMatchCondition(const TaskCfg &task_cfg, Args&& ... args) const
{
	int idx = 0;
	std::vector<uint32> vec_para;
	auto f = [&vec_para](uint32 para)
	{
		vec_para.push_back(para);
	};
	std::initializer_list<int>{(f(std::forward<Args>(args)), 0)...};

	// 判断 PARA_OPT_LIST 是否为 nullptr 类型或者不是数组类型
   if constexpr (std::is_array_v<decltype(TaskTypeInfo<task_type>::PARA_OPT_LIST)>)
   {
	   #define MyArrayLen(array) (sizeof(array)/sizeof((array)[0]))
	   for (size_t i = 0; i < MyArrayLen(TaskTypeInfo<task_type>::PARA_OPT_LIST); i++)
	   {
		   TaskParaOpt logic= TaskTypeInfo<task_type>::PARA_OPT_LIST[i];
		   uint32 cfg_para = GetCfgPara(task_cfg, idx);
		   uint32 para = vec_para[idx];
		   if (!IsLogicOk(logic, para, cfg_para))
		   {
			   return false;
		   }
		   ++idx;
	   }
	   #undef MyArrayLen
   }
	return true;
}
